package cn.zyl.demo.common.tools;

import cn.zyl.demo.common.enums.HexEnum;

/**
 * HexTool类说明:byte字节工具类
 *
 * @author wsz
 * @version v1.0
 * @date 2020-07-22
 */
public class HexTool {
    private HexTool(){throw new IllegalAccessError("HexTool");}
    /** 数字2*/
    private final static int TWO = 2;
    /** 字节最大值*/
    private final static int BYTE_LEN_MAX = 256;
    /** 0字符*/
    private final static char ZERO_STR = '0';
    /**
     * 将byte数组转换为表示16进制值的字符串
     *
     * @param bytes 需要转换的byte数组
     * @return 转换后的字符串
     */
    public static  String byteArr2HexStr(byte[] bytes) {
        int length = bytes.length;
        // 每个byte用两个字符才能表示，所以字符串的长度是数组长度的两倍..一个byte转成16进制最多不会超过两位。FF
        StringBuilder sb = new StringBuilder(length * TWO);
        for (int byte_ : bytes) {
            // 把负数转换为正数
            while (byte_ < 0) { byte_ = byte_ + BYTE_LEN_MAX; }
            // 小于0F的数需要在前面补0
            if (byte_ < 16) { sb.append(ZERO_STR); }
            // 16代表进制
            sb.append(Integer.toString(byte_, HexEnum.HEX_16.getHex()));
        }
        return sb.toString();
    }

    /**
     * 将表示16进制值的字符串转换为byte数组
     *
     * @param s 需要转换的字符串
     * @return 转换后的byte数组
     */
    public static  byte[] hexStr2ByteArr(String s) {
        byte[] bytes = s.getBytes();
        int length = bytes.length;
        // 两个字符表示一个字节，所以字节数组长度是字符串长度除以2
        byte[] res = new byte[length / TWO];
        for (int i = 0; i < length; i = i + TWO) {
            String tmp = new String(bytes, i, TWO);
            res[i / TWO] = (byte) Integer.parseInt(tmp, HexEnum.HEX_16.getHex());
        }
        return res;
    }
    /** 支持的最小进制*/
    private final static int MIN_RADIX = 2;
    /** 默认10进制*/
    private final static int DEF_RADIX = 10;
    /** 中间36进制*/
    private final static int MIDDLE_RADIX = 36;
    /** 支持的最大进制*/
    private final static int MAX_RADIX = 65;
    /** 0-9a-zA-Z表示62进制内的 0到63。*/
    private static final String NUM_64 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/";
    private static final char STR_MINUS = '-';
    /**
     * 返回一字符串，包含 number 以 toRadix 进制的表示。<br />
     * number 本身的进制由 fromRadix 指定。fromRadix 和 toRadix 都只能在 2 和 62 之间（包括 2 和 62）。<br />
     * 高于十进制的数字用字母 a-zA-Z 表示，例如 a 表示 10，b 表示 11 以及 Z 表示 62。<br />
     * @param number    需要转换的数字
     * @param fromRadix     输入进制,2-64
     * @param toRadix       输出进制,2-64
     * @return  指定输出进制的数字
     */
    public static String baseConvert(String number, int fromRadix, int toRadix) {
        long dec = any2Dec(number, fromRadix);
        return dec2Any(dec, toRadix);
    }

    /**
     * 返回一字符串，包含 十进制 number 以 radix 进制的表示。
     * @param dec       需要转换的数字
     * @param toRadix    输出进制。当不在转换范围内时，此参数会被设定为 2，以便及时发现。
     * @return    指定输出进制的数字
     */
    public static String dec2Any(long dec, int toRadix) {
        if (toRadix < MIN_RADIX || toRadix > MAX_RADIX) { toRadix = DEF_RADIX; }
        if (toRadix == DEF_RADIX) { return String.valueOf(dec); }
        // -Long.MIN_VALUE 转换为 2 进制时长度为65
        char[] buf = new char[MAX_RADIX];
        int charPos = MAX_RADIX-1;
        boolean isNegative = (dec < 0);
        if (!isNegative) { dec = -dec; }
        while (dec <= -toRadix) {
            buf[charPos--] = NUM_64.charAt((int) (-(dec % toRadix)));
            dec = dec / toRadix;
        }
        buf[charPos] = NUM_64.charAt((int) (-dec));
        if (isNegative) { buf[--charPos] = STR_MINUS; }
        String res = new String(buf, charPos, (65 - charPos));
        //36进制以下不区分大小写，统一转为大写
        if(toRadix <= MIDDLE_RADIX){return res.toUpperCase();}
        return res;
    }
    /**
     * 返回一字符串，包含 number 以 10 进制的表示。<br />
     * fromBase 只能在 2 和 62 之间（包括 2 和 62）。
     * @param number    输入数字
     * @param fromRadix    输入进制,2-64
     * @return  十进制数字
     */
    public static long any2Dec(String number, int fromRadix) {
        if (fromRadix < MIN_RADIX || fromRadix > MAX_RADIX) { fromRadix = DEF_RADIX; }
        //36进制以下不区分大小写，统一转为小写
        if(fromRadix <= MIDDLE_RADIX){number = number.toLowerCase();}
        long dec = 0,digitValue;
        int len = number.length() - 1;
        for (int t = 0; t <= len; t++) {
            digitValue = NUM_64.indexOf(number.charAt(t));
            dec = dec * fromRadix + digitValue;
        }
        return dec;
    }
    public static void main(String[] args) {
        long hex10 = 65L;
        int min = 2,max = 36;
        for (int i = min; i < max; i++) {
            //2-36进制
            System.out.println(i+"进制："+Long.toString(hex10,i));
        }
        String str10 = "1111010";
        max = 64;
        for (int i = min; i < max; i++) {
            //2-64进制
            System.out.println(i+"-进制："+ baseConvert(str10, min, i));
        }
    }
}
